import math
import random
from math import comb
from collections import Counter
#概率牌型2.0

def build_position_mapping():
    """构建牌位置到点型的映射字典"""
    columns = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L']
    mapping = {}

    # 第一行（A1-L1）
    row1 = ['***', '**', '***-1', '**', '**', '**-2', '**-2', '***-1', '**-1', '**-2', '***-3', '***']
    for i, col in enumerate(columns):
        mapping[f"{col}1"] = row1[i]

    # 第二行（A2-L2）
    row2 = ['*-1', '*-1', '*-1', '*-2', '*-2', '*-3', '*-2', '**', '*-3', '*-3', '**', '']
    for i, col in enumerate(columns):
        mapping[f"{col}2"] = row2[i]

    # 第三行（A3-L3）
    row3 = [''] * 12
    row3[10] = '*-2'  # K3是*
    for i, col in enumerate(columns):
        mapping[f"{col}3"] = row3[i]

    # 第四行（A4-L4）
    row4 = [''] * 12
    for i, col in enumerate(columns):
        mapping[f"{col}4"] = row4[i]

    return mapping


def exact_probability(k1, k2, k3, n):
    """精确概率计算"""
    # 输入验证
    if k1 + k2 + k3 != n or n < 5 or n > 8:
        return 0.0
    if k1 > 5 or k2 > 9 or k3 > 10:
        return 0.0

    total_success = 0

    # 遍历所有可能的移除组合
    for a in range(0, 5 - k1 + 1):  # ***移除数量
        remaining_3 = 5 - a
        if remaining_3 < k1: continue

        for b in range(0, 9 - k2 + 1):  # **移除数量
            remaining_2 = 9 - b
            if remaining_2 < k2: continue

            for c in range(0, 10 - k3 + 1):  # *移除数量
                remaining_1 = 10 - c
                if remaining_1 < k3: continue

                m = a + b + c
                if m > 8: continue  # 总移除有效牌不超过8

                # 计算剩余有效牌数
                remaining_effective = remaining_3 + remaining_2 + remaining_1
                if remaining_effective < n: continue  # 剩余有效牌不足时跳过

                # 计算组合数
                remove_comb = comb(5, a) * comb(9, b) * comb(10, c)  # 有效牌移除组合
                blank_comb = comb(24, 8 - m) if (8 - m) >= 0 else 0  # 空白牌移除组合
                draw_comb = comb(remaining_3, k1) * comb(remaining_2, k2) * comb(remaining_1, k3)  # 正确抽取组合
                total_comb = remove_comb * blank_comb * draw_comb * comb(remaining_effective, n)

                total_success += total_comb

    # 计算总可能组合
    total_possible = comb(48, 8) * comb(40, n)
    return total_success / total_possible if total_possible != 0 else 0


def main():
    position_mapping = build_position_mapping()
    full_deck = [point for pos, point in position_mapping.items() if point]  # 24张有效牌

    # 用户输入处理
    user_input = input("请输入牌型组合（例：A1 B2 C3 K3 L2）: ").strip().upper().split()
    if not 2 <= len(user_input) <= 10:
        print("错误：牌数需在5-8张")
        return

    # 转换为点型
    required_points = []
    for pos in user_input:
        if pos not in position_mapping:
            print(f"错误：无效位置 {pos}")
            return
        point = position_mapping[pos]
        if not point:
            print(f"错误：{pos} 是空白牌")
            return
        required_points.append(point)

    # 统计点型需求
    target_counts = Counter(required_points)
    k1 = target_counts.get('***', 0)
    k2 = target_counts.get('**', 0)
    k3 = target_counts.get('*', 0)
    n = len(required_points)

    # 蒙特卡罗模拟
    mc_total = 1000000
    mc_success = 0

    for _ in range(mc_total):
        temp = full_deck.copy() + [''] * 24  # 完整48张牌（含空白）
        random.shuffle(temp)
        remaining = [p for p in temp[:-8] if p]  # 剩余有效牌

        # 检查剩余牌是否满足需求
        remain_counts = Counter(remaining)
        valid = all(remain_counts.get(pt, 0) >= cnt for pt, cnt in target_counts.items())

        if valid and len(remaining) >= n:
            # 尝试抽取
            try:
                selected = random.sample(remaining, n)
                if Counter(selected) == target_counts:
                    mc_success += 1
            except:
                continue

    # 计算两种概率
    mc_prob = mc_success / mc_total
    exact_prob = exact_probability(k1, k2, k3, n)

    # 显示结果
    print("\n" + "=" * 40)
    print(f"目标牌型: {target_counts}")
    print(f"蒙特卡罗结果（{mc_total}次）: {mc_prob:.6f} ({mc_prob * 100:.4f}%)")
    print(f"精确计算结果: {exact_prob:.6f} ({exact_prob * 100:.4f}%)")
    print("=" * 40)


if __name__ == "__main__":
    main()